package com.ruoyi.web.money;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.system.domain.MoneyFundTemperature;
import com.ruoyi.web.money.common.CacheKey;
import com.ruoyi.web.money.model.ExcelModel;
import com.ruoyi.web.money.service.IMoneyFundTemperatureService;

/**
 * @author liyabin
 * @date 2021/3/16
 */
public class ReadListener extends AnalysisEventListener<ExcelModel> implements CacheKey {


    private RedisTemplate<String, String> redisTemplate;


    private IMoneyFundTemperatureService iMoneyFundTemperatureService;

    private List<MoneyFundTemperature> data;

    private List<ExcelModel> temp;

    private final int SIZE = 3000;

    private Long fundId;


    public ReadListener(RedisTemplate redisTemplate, IMoneyFundTemperatureService service, Long fundId) {
        this.redisTemplate = redisTemplate;
        this.iMoneyFundTemperatureService = service;
        data = new ArrayList<>(SIZE);
        temp = new ArrayList<>(SIZE);
        this.fundId = fundId;
    }

    @Override
    public void invoke(ExcelModel excelModel, AnalysisContext analysisContext) {
        temp.add(excelModel);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        if (!CollectionUtils.isEmpty(temp)) {
            boolean isPB = temp.stream().anyMatch(model -> !StringUtils.isEmpty(model.getPb()));
            boolean isPE = !isPB && temp.stream().anyMatch(model -> !StringUtils.isEmpty(model.getPe()));
            ListOperations<String, String> operations = redisTemplate.opsForList();
            List<String> range = Optional.ofNullable(operations.range(cacheKey(FUND_LIST_KEY, fundId), 0, -1))
                    .orElse(new ArrayList<>());
            List<MoneyFundTemperature> cache = range.stream().map(JSONObject::parseObject)
                    .map(bean -> bean.toJavaObject(MoneyFundTemperature.class))
                    .collect(Collectors.toList());
            // 如果为空-
            if (isPB) {
                cache = compute(ExcelModel::getPb, MoneyFundTemperature::setPbTemperature, MoneyFundTemperature::setPb, cache);
            }
            // 覆盖旧 PE数据
            if (isPE) {
                cache = compute(ExcelModel::getPe, MoneyFundTemperature::setPeTemperature, MoneyFundTemperature::setPe, cache);
            }
            cache(cache);
            boolean cachePB = cache.stream().anyMatch(model -> !StringUtils.isEmpty(model.getPb()));
            boolean cachePE = cache.stream().anyMatch(model -> !StringUtils.isEmpty(model.getPe()));
            // 已经是所有都OK了。
            if (cachePB && cachePE) {
                Consumer<MoneyFundTemperature> consumer = entity -> entity.setTemperature((entity.getPeTemperature() + entity.getPbTemperature()) / 2);
                cache.stream().filter(bean -> bean.getPb() > 0 && bean.getPe() > 0)
                        .forEach(consumer);
                cache.stream().forEach(iMoneyFundTemperatureService::insertMoneyFundTemperature);
                redisTemplate.delete(cacheKey(FUND_LIST_KEY, today(), fundId));

            }

        }
    }

    private List<MoneyFundTemperature> compute(Function<ExcelModel, Double> mapTemp,
                                               BiConsumer<MoneyFundTemperature, Double> consumerValue,
                                               BiConsumer<MoneyFundTemperature, Double> consumerKey,
                                               List<MoneyFundTemperature> data) {
        List<MoneyFundTemperature> result = new ArrayList<>(data.size());
        Map<String, MoneyFundTemperature> map = data.stream().distinct()
                .collect(Collectors.toMap(MoneyFundTemperature::getTime, bean -> bean));
        temp = temp.stream().filter(bean -> bean.getPe() != null || bean.getPb() != null).collect(Collectors.toList());
        // 排好序计算 温度
        List<ExcelModel> sortList = temp.stream().sorted(Comparator.comparing(mapTemp))
                .collect(Collectors.toList());
        int size = temp.size();
        //  excel 中 PERCENTRANK  小于他的/总数 - 1
        for (int i = 0; i < size; i++) {
            double less = i;
            int index = i;
            ExcelModel excelModel = sortList.get(i);
            while (index != 0 && Optional.of(excelModel).map(mapTemp).orElse(0d)
                    .equals(Optional.of(sortList.get(index - 1)).map(mapTemp).orElse(0d))) {
                index--;
            }
            Double value = ((double) index / (size - 1)) * 100;
            MoneyFundTemperature item = Optional.ofNullable(map.get(excelModel.getTime()))
                    .orElse(MoneyFundTemperature.builder().time(excelModel.getTime()).fundId(fundId).build());
            consumerValue.accept(item, value);
            consumerKey.accept(item, Optional.of(excelModel).map(mapTemp).orElse(0d));
            result.add(item);
        }
        return result;
    }

    private void cache(List<MoneyFundTemperature> cache) {
        redisTemplate.delete(cacheKey(FUND_LIST_KEY, fundId));
        ListOperations<String, String> operations = redisTemplate.opsForList();
        String[] value = cache.stream().map(JSONObject::toJSONString).toArray(String[]::new);
        operations.rightPushAll(cacheKey(FUND_LIST_KEY, fundId), value);
        redisTemplate.expire(cacheKey(FUND_LIST_KEY, fundId), 7, TimeUnit.DAYS);
    }


}
